package com.whales.shared.controller;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

import anyframe.common.Page;

import com.whales.shared.exception.BaseException;
import com.whales.shared.service.GenericService;

/**
 * <p>Generic Controller 추상화</p>
 * 
 * <p>
 * 본 Controller는 중복되는 코드를 최소화 하기 위한  추상화 클래스이며,<br>
 * 상속한 서브 클래스에서 필요한 경우 Override해서 사용할 수 있습니다.
 * </p>
 * @클래스명 : GenericController   
 * @작성자 : sw775.park
 * @작성일 : 2012. 7. 11.     
 * @설명 : 
 * @수정이력 - 수정일,수정자,수정내용  					   
 * 
 */
@SessionAttributes("model")
public abstract class GenericController<EVO, PVO, GS extends GenericService<EVO, PVO>> extends BaseController {
    /** DL(Dependency Lookup)을 위한 ApplicationContext */
    @Inject
    ApplicationContext applicationContext;
    /** Generic Service */
    protected GS service;
    /** RequestMapping된 URL */
    protected String requestUrl;
    /** Entity VO */
    protected Class<EVO> entityVo;
    /** Parameter VO */
    protected Class<PVO> paramVo;
    /** Service Class */
    protected Class<GS> serviceClass;
    protected final String SUCCESS_JSON_STRING = "{success : true}"; 
    
    /**
     * GenericController 생성자
     * @생성자명 : GenericController
     * @작성자 : sw775.park  
     * @작성일 : 2012. 7. 11. 
     * @설명 : 
     * @수정이력 - 수정일,수정자,수정내용 
     *     
     */
    @SuppressWarnings("unchecked")
    public GenericController() {
        RequestMapping rm = this.getClass().getAnnotation(RequestMapping.class);
        this.requestUrl = rm.value()[0];
        this.entityVo = this.getClassTypeInGeneric(this.getClass(), 0);
        this.paramVo = this.getClassTypeInGeneric(this.getClass(), 1);
        this.serviceClass = this.getClassTypeInGeneric(this.getClass(), 2);
    }
    
    /**
     * Controller가 인스턴스화 될때
     * 처음 호출되는 메소드
     * 주로 서비스 객체를 DL 한다.
     * @메소드명 : setUp   					   
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 11.	
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @PostConstruct
    public void setUp(){
        this.service = this.applicationContext.getBean(this.serviceClass);
    }
    
    /**
     * 등록 화면
     * @메소드명 : addForm   					   
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 11.
     * @param model
     * @throws Exception	
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @RequestMapping 
    public void addForm(Model model) throws Exception{ 
        model.addAttribute("model", this.entityVo.newInstance());
    }
    
    /**
     * 등록 form submit
     * @메소드명 : addFormSubmit   					   
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 10.
     * @param paramVo	
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @RequestMapping 
    public String addFormSubmit(Model model,@ModelAttribute("model")PVO paramVo, SessionStatus sessionStatus) throws Exception{ 
        this.service.create(paramVo);
        sessionStatus.setComplete();
        return "redirect:" + requestUrl + "/list.do";
    }
    
    
    /**
     * 등록
     * @메소드명 : create                          
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 17.
     * @param paramVo
     * @throws Exception    
     * @설명 : ※ View가 없습니다.
     * @수정이력 - 수정일,수정자,수정내용                    
     *                                         
     */    
    @RequestMapping 
    public ResponseEntity<String> create(PVO paramVo) throws Exception {
    	try{
			this.service.create(paramVo);
		}catch(BaseException e){
//			return this.getFailJsonString(e);
			return getResponseEntity(this.getFailJsonString(e));
		}
        return getResponseEntity(SUCCESS_JSON_STRING);
    }
    
    /**
     * 삭제
     * @메소드명 : delete   					   
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 17.
     * @param paramVo
     * @throws Exception	
     * @설명 : ※ View가 없습니다.
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @RequestMapping 
    public @ResponseBody void delete(PVO paramVo) throws Exception {
        this.service.delete(paramVo);
    }
    
    /**
     * 리스트 화면
     * @메소드명 : list   					   
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 17.
     * @param model
     * @param paramVo
     * @throws Exception	
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @RequestMapping
    public @ResponseBody List<EVO> list(Model model, PVO paramVo) throws Exception {
        return this.service.getList(paramVo);
    }
    
    /**
     * Page 리스트 화면
     * @메소드명 : list                        
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 17.
     * @param model
     * @param paramVo
     * @param pageIndex
     * @param pageSize
     * @throws Exception	
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @RequestMapping
    public @ResponseBody Map<String, Object> pageList(PVO paramVo) throws Exception {
    	Page page = this.service.getPageList(paramVo);
    	Map<String, Object> pageMap = new HashMap<String, Object>();
    	pageMap.put("totalCount", page.getTotalCount());
    	pageMap.put("list", page.getList());
        return pageMap;
    }
    
    /**
     * 단건조회
     * @param paramVo
     * @return
     * @throws Exception
     */
    @RequestMapping
    public @ResponseBody Map<String, Object> findInfo(PVO paramVo) throws Exception {
    	Map<String, Object> returnMap = new HashMap<String, Object>();
		returnMap.put("success", true);
		returnMap.put("data", this.service.findInfo(paramVo));
		return returnMap;
    }
    
    /**
     * 수정 화면
     * @메소드명 : editForm                         
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 11.
     * @param model
     * @param paramVo
     * @throws Exception    
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용                    
     *                                         
     */
    @RequestMapping 
    public void editForm(Model model, PVO paramVo) throws Exception{ 
        model.addAttribute("model", this.service.findInfo(paramVo));
    }
    
    /**
     * 수정 form submit
     * @메소드명 : editFormSubmit                       
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 10.
     * @param paramVo   
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용                    
     *                                         
     */
    @RequestMapping 
    public String editFormSubmit(PVO paramVo) throws Exception{
        this.service.update(paramVo);
        return "redirect:" + requestUrl + "/list.do";
    }        
    
    /**
     * update
     * @메소드명 : update   					   
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 17.
     * @param paramVo
     * @throws Exception	
     * @설명 : ※ View가 없습니다.
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @RequestMapping 
    public @ResponseBody String update(PVO paramVo) throws Exception {  
        this.service.update(paramVo);
        return SUCCESS_JSON_STRING;
    }
    
    /**
     * Generic에 정의된 Class type을 반환 한다.
     * @메소드명 : getClassTypeInGeneric   					   
     * @작성자 : sw775.park   
     * @작성일 : 2012. 7. 11.
     * @param clazz
     * @param index
     * @return	
     * @설명 :
     * @수정이력 - 수정일,수정자,수정내용					   
     *										   
     */
    @SuppressWarnings("rawtypes")
	final Class getClassTypeInGeneric(Class clazz, int index) {
        ParameterizedType genericSuperclass = (ParameterizedType) clazz.getGenericSuperclass();
        Type wantedClassType = genericSuperclass.getActualTypeArguments()[index];
        if (wantedClassType instanceof ParameterizedType) {
            return (Class) ((ParameterizedType) wantedClassType).getRawType();
        } else {
            return (Class) wantedClassType;
        }
    }
    
    protected String getFailJsonString(BaseException e) throws Exception{
    	return "{success : false, msg : '"+ e.getMessage() +"'}";
    }
    
    protected ResponseEntity<String> getResponseEntity(String message) {
    	HttpHeaders responseHeaders = new HttpHeaders();
   	 	responseHeaders.add("Content-Type", "text/html; charset=UTF-8");
   	 	return new ResponseEntity<String>(message, responseHeaders, HttpStatus.CREATED);
    }
}
